// Node.js的核心模块，专门用来处理文件路径
const path = require("path");
const os = require("os");
// 配置 eslint
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
// 处理 html 资源
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 提取 css 成单独文件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// preload 预加载
const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");

// cpu核数-这边得到的是线程数
const threads = os.cpus().length;
// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {
  return [
    MiniCssExtractPlugin.loader,
    "css-loader",
    {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: [
            "postcss-preset-env", // 能解决大多数样式兼容性问题
          ],
        },
      },
    },
    preProcessor,
  ].filter(Boolean);
};

module.exports = {
  // 模式
  mode: "development",
  // 打包编译速度快，只包含行映射
  devtool: "cheap-module-source-map",
  // 入口
  entry: "./src/index.js",
  // 输出
  output: {
    // path: 文件输出目录，必须是绝对路径
    // path.resolve()方法返回一个绝对路径
    // __dirname 当前文件的文件夹绝对路径
    path: undefined,
    // filename: 输出文件名
    // filename: "main.js",
    filename: "static/js/[name].js",
    chunkFilename: "static/js/[name].chunk.js", // 动态导入输出资源命名方式
    assetModuleFilename: "static/media/[name].[hash:8][ext]", // 图片、字体等资源命名方式（注意用hash）
    // clean: true, // 开发模式没有输出，不需要清空输出结果
  },
  // 加载器
  module: {
    rules: [
      {
        oneOf: [
          // 匹配到一个rule之后就不在匹配了
          {
            // 用来匹配 .css 结尾的文件
            test: /\.css$/,
            // use 数组里面 Loader 执行顺序是从右到左
            // use: ["style-loader", "css-loader"],
            // use: [MiniCssExtractPlugin.loader, "css-loader"],
            use: getStyleLoaders(),
          },
          {
            test: /\.less$/,
            // use: ["style-loader", "css-loader", "less-loader"],
            // use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
            use: getStyleLoaders("less-loader"),
          },
          {
            test: /\.s[ac]ss$/,
            // use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
            use: getStyleLoaders("sass-loader"),
          },
          {
            // 处理图片资源
            test: /\.(png|jpe?g|gif|webp)$/,
            type: "asset",
            // 默认小于 8kb 的文件将会视为 inline 模块类型，否则会被视为 resource 模块类型
            // inline 类型的会作为 dataURI 内联到 bundle 中
            // resource 类型的是作为一个单独的文件
            parser: {
              dataUrlCondition: {
                maxSize: 10 * 1024, // 小于 10kb 的图片会被 base64 处理
              },
            },
            // 使用 output 中的 assetModuleFilename 替代
            // generator: {
            //   // 将图片文件输出到 static/imgs 目录中
            //   // 将图片文件命名 [hash:8][ext][query]
            //   // [hash:8]: hash 值取8位
            //   // [ext]: 使用之前的文件扩展名
            //   // [query]: query 参数，访问URL上的
            //   filename: "static/imgs/[hash:8][ext][query]",
            // },
          },
          {
            // 处理字体图标资源和其他资源
            test: /\.(ttf|woff2?|map4|map3|avi)$/,
            type: "asset/resource",
            // 使用 output 中的 assetModuleFilename 替代
            // generator: {
            //   filename: "static/media/[hash:8][ext][query]",
            // },
          },
          // type: "asset/resource" 相当于file-loader, 将文件转化成 Webpack 能识别的资源，其他不做处理
          // type: "asset" 相当于url-loader, 将文件转化成 Webpack 能识别的资源，同时小于某个大小的资源会处理成 data URI 形式
          {
            test: /\.js$/,
            exclude: /node_modules/, // 排除node_modules代码不编译
            // loader: "babel-loader",
            // options: {
            //   cacheDirectory: true, // 开启babel编译缓存
            //   cacheCompression: false, // 缓存文件不要压缩
            //   // babel辅助代码作为独立模块避免重复引入，减少代码体积
            //   plugins: ["@babel/plugin-transform-runtime"],
            // },
            use: [
              {
                loader: "thread-loader", // 开启多进程
                options: {
                  workers: threads, // 数量
                },
              },
              {
                loader: "babel-loader",
                options: {
                  cacheDirectory: true, // 开启babel编译缓存
                  cacheCompression: false, // 缓存文件不要压缩
                  // babel辅助代码作为独立模块避免重复引入，减少代码体积
                  plugins: ["@babel/plugin-transform-runtime"],
                },
              },
            ],
          },
        ],
      },
    ],
  },
  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件有两个特点：1. 内容和源文件一致 2. 自动引入打包生成的js等资源
      template: path.resolve(__dirname, "../public/index.html"),
    }),
    new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules",
      cache: true, // 开启缓存
      // 缓存目录
      cacheLocation: path.resolve(__dirname, "../node_modules/.cache/.eslintcache"),
      threads, // 开启多进程
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: "static/css/[name].[contenthash:8].css",
      chunkFilename: "static/css/[name].[contenthash:8].chunk.css",
    }),
    // 预加载
    new PreloadWebpackPlugin({
      rel: "preload", // preload兼容性更好
      as: "script",
      // rel: 'prefetch' // prefetch兼容性更差, 不需要 as: "script"
    }),
  ],
  optimization: {
    // 代码分割配置，提取重复引入的代码和动态导入的文件
    splitChunks: {
      chunks: "all",
    },
    // 提取 runtime 文件
    runtimeChunk: {
      name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime 文件命名规则
    },
  },
  // 开发服务器
  devServer: {
    host: "localhost", // 启动服务器域名
    port: "3000", // 启动服务器端口号
    open: true, // 是否自动打开浏览器
  },
};
